pangolin/src/app/[orgId]/settings/access/users/[userId]/access-controls/page.tsx

219 lines
8.1 KiB
TypeScript

"use client";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from "@app/components/ui/form";
import { toast } from "@app/hooks/useToast";
import { zodResolver } from "@hookform/resolvers/zod";
import { SetUserRolesResponse } from "@server/routers/user";
import { AxiosResponse } from "axios";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { ListRolesResponse } from "@server/routers/role";
import { userOrgUserContext } from "@app/hooks/useOrgUserContext";
import { useParams } from "next/navigation";
import { Button } from "@app/components/ui/button";
import {
SettingsContainer,
SettingsSection,
SettingsSectionHeader,
SettingsSectionTitle,
SettingsSectionDescription,
SettingsSectionBody,
SettingsSectionForm,
SettingsSectionFooter
} from "@app/components/Settings";
import { formatAxiosError } from "@app/lib/api";
import { createApiClient } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { Tag, TagInput } from "@app/components/tags/tag-input";
const formSchema = z.object({
username: z.string(),
roles: z
.array(
z.object({
id: z.string(),
text: z.string()
})
)
.min(1, { message: "Please select a role" })
});
export default function AccessControlsPage() {
const { orgUser: user } = userOrgUserContext();
const api = createApiClient(useEnvContext());
const { orgId } = useParams();
const [loading, setLoading] = useState(false);
const [allRoles, setAllRoles] = useState<{ id: string; text: string }[]>(
[]
);
const [activeRolesTagIndex, setActiveRolesTagIndex] = useState<
number | null
>(null);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
username: user.username!,
roles: []
}
});
useEffect(() => {
async function fetchRoles() {
const res = await api
.get<AxiosResponse<ListRolesResponse>>(`/org/${orgId}/roles`)
.catch((e) => {
console.error(e);
toast({
variant: "destructive",
title: "Failed to fetch roles",
description: formatAxiosError(
e,
"An error occurred while fetching the roles"
)
});
});
if (res?.status === 200) {
setAllRoles(
res.data.data.roles.map((role) => ({
id: role.roleId.toString(),
text: role.name
}))
);
}
}
fetchRoles();
form.setValue(
"roles",
user.roles.map((i) => ({
id: i.id.toString(),
text: i.name
}))
);
}, []);
async function onSubmit(values: z.infer<typeof formSchema>) {
setLoading(true);
const res = await api
.post<
AxiosResponse<SetUserRolesResponse>
>(`/org/${user.orgId}/user/${user.userId}/roles`, { roleIds: values.roles.map((r) => parseInt(r.id)) })
.catch((e) => {
toast({
variant: "destructive",
title: "Failed to add user to role",
description: formatAxiosError(
e,
"An error occurred while adding user to the role."
)
});
});
if (res && res.status === 200) {
toast({
variant: "default",
title: "User saved",
description: "The user has been updated."
});
}
setLoading(false);
}
return (
<SettingsContainer>
<SettingsSection>
<SettingsSectionHeader>
<SettingsSectionTitle>Access Controls</SettingsSectionTitle>
<SettingsSectionDescription>
Manage what this user can access and do in the
organization
</SettingsSectionDescription>
</SettingsSectionHeader>
<SettingsSectionBody>
<SettingsSectionForm>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-4"
id="access-controls-form"
>
<FormField
control={form.control}
name="roles"
render={({ field }) => (
<FormItem className="flex flex-col items-start">
<FormLabel>Roles</FormLabel>
<FormControl>
<TagInput
{...field}
activeTagIndex={
activeRolesTagIndex
}
setActiveTagIndex={
setActiveRolesTagIndex
}
placeholder="Select a role"
size="sm"
tags={
form.getValues().roles
}
setTags={(newRoles) => {
form.setValue(
"roles",
newRoles as [
Tag,
...Tag[]
]
);
}}
enableAutocomplete={true}
autocompleteOptions={
allRoles
}
allowDuplicates={false}
restrictTagsToAutocompleteOptions={
true
}
sortTags={true}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
</SettingsSectionForm>
</SettingsSectionBody>
<SettingsSectionFooter>
<Button
type="submit"
loading={loading}
disabled={loading}
form="access-controls-form"
>
Save Access Controls
</Button>
</SettingsSectionFooter>
</SettingsSection>
</SettingsContainer>
);
}